fix(claude): enable 1M context detection inside moat containers#308
Open
abezzub-dr wants to merge 4 commits into
Open
fix(claude): enable 1M context detection inside moat containers#308abezzub-dr wants to merge 4 commits into
abezzub-dr wants to merge 4 commits into
Conversation
Three issues combined to prevent Claude Code from detecting 1M context window subscription capabilities inside moat: 1. CLAUDE_CODE_OAUTH_TOKEN=moat-proxy-injected didn't look like a real OAuth token (sk-ant-oat01-*), so Claude Code didn't recognize it as an OAuth session. 2. /api/bootstrap returned account:null with setup-tokens (lacking scopes), so Claude Code couldn't detect subscription tier. 3. .credentials.json had no subscription metadata (subscriptionType, rateLimitTier). Fix by: - Removing CLAUDE_CODE_OAUTH_TOKEN env var; using .credentials.json with a proper sk-ant-oat01-* placeholder token instead - Caching the full /api/bootstrap response at grant time (when the host's OAuth token is fresh) and at run time (as fallback) - Serving the cached bootstrap via the proxy response transformer - Including subscription metadata in .credentials.json and synthetic /api/oauth/profile responses The grant-time caching is the key reliability improvement: at grant time the user has just authenticated, so the host's short-lived OAuth token is valid. The cached bootstrap persists in the encrypted credential store and is available at run time even if the host token has since expired.
Setup-token grants don't set ExpiresAt on the credential, leaving it as the zero time.Time. WriteCredentialsFile called UnixMilli() on this, producing -62135596800000 (year 0001) in .credentials.json. Claude Code interpreted this as an expired credential, showing "not logged in" and "API Usage Billing" instead of recognizing the subscription session. Use a 1-year-ahead timestamp when ExpiresAt is zero, since setup-tokens are long-lived and the placeholder credential just needs to look valid.
…data present The cached bootstrap was served unconditionally, overriding even valid responses from full-scope tokens. Now the proxy reads the real response first: if it contains non-null account data, it passes through untouched. The cached version is only used as a fallback when the response is degraded (setup-token returning account:null, or non-200). This prevents stale subscription data when tokens get refreshed, and documents the remaining staleness limitation for setup-token grants.
…file, add tests - Consolidate cred.Metadata nil-check to single init at top of cacheBootstrapForCredential instead of three conditional branches - Replace fmt.Sprintf JSON construction with json.Marshal via typed oauthProfileResponse struct in createOAuthResponseWithMeta — handles special characters correctly and omits empty subscription fields - Add TestCreateOAuthResponseWithMeta (5 cases) covering subscription metadata, omitempty, and special character handling - Add TestOAuthEndpointTransformer_403Workarounds (3 cases) covering 403 transformation, passthrough, and non-workaround endpoints
Collaborator
|
Do you mind if we split this up. The placeholder token and expiresAt seem simple an clearly correct. I'm a little more worried about the bootstrap caching and subscription stuff. For the second part I think we should also orient around grant-time capture, run-time synthesize. I like the current implementation has cross |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/api/bootstrapresponse at grant time (when host OAuth token is fresh) and serve it via proxysk-ant-oat01-*placeholder in.credentials.jsoninstead ofCLAUDE_CODE_OAUTH_TOKENenv varContext
Claude Code calls
/api/bootstrapat startup to determine account capabilities. Setup-tokens (fromclaude setup-token) lack the scopes for this endpoint to return proper account info and feature flags, causing Claude Code to report "Opus 4.6 with 1M context is not available for your account."This PR caches the full bootstrap response at grant time (when the host's short-lived OAuth token is guaranteed fresh), stores it in the encrypted credential store, and has the proxy serve it instead of the limited setup-token response. As a fallback, it also attempts to fetch bootstrap at run time if the host token is still valid.
Additionally, the
CLAUDE_CODE_OAUTH_TOKENenv var (set tomoat-proxy-injected) is replaced with a proper.credentials.jsoncontaining ansk-ant-oat01-*prefix placeholder, so Claude Code recognizes the session as OAuth-authenticated.Test plan
moat grant claude --verboseand verifycached bootstrap response at grant timeappears with non-zerobootstrap_lenmoat claudeand verify Claude Code reports 1M context availabilitymoat claudestill works withanthropic(API key) grants (no regression)make test-unitmake lint🤖 Generated with Claude Code